package com.micro.fast.eureka.server.controller;

import com.micro.fast.boot.starter.common.response.ServerResponse;
import com.netflix.appinfo.AmazonInfo;
import com.netflix.appinfo.ApplicationInfoManager;
import com.netflix.appinfo.DataCenterInfo;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.config.ConfigurationManager;
import com.netflix.discovery.shared.Application;
import com.netflix.discovery.shared.Pair;
import com.netflix.eureka.EurekaServerContext;
import com.netflix.eureka.EurekaServerContextHolder;
import com.netflix.eureka.cluster.PeerEurekaNode;
import com.netflix.eureka.registry.PeerAwareInstanceRegistry;
import com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl;
import com.netflix.eureka.resources.StatusResource;
import com.netflix.eureka.util.StatusInfo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URI;
import java.util.*;

/**
 * restful操作eureka的接口
 *
 * @author lsy
 */
@RestController
@RequestMapping({"${eureka.restful.path:/restful/eureka}"})
@Api(description = "注册中心的restful接口")
public class RestfulEurekaController {


    @Value("${eureka.restful.path:/restful/eureka}")
    private String restfulPath = "/restful/eureka";

    /**
     * 实例管理对象
     */
    private ApplicationInfoManager applicationInfoManager;

    public RestfulEurekaController(ApplicationInfoManager applicationInfoManager) {
        this.applicationInfoManager = applicationInfoManager;
    }

    @GetMapping("/apps")
    @ApiOperation("获取所有的服务实例的简单信息")
    public ServerResponse apps(HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<>();
        //获取所有服务的信息
        populateApps(map);
        return ServerResponse.successMsgData("获取所有实例成功", map);
    }

    @GetMapping("/apps/{instanceId}")
    @ApiOperation("根据主机名获取某个实例的信息")
    public ServerResponse getInstance(@PathVariable("instanceId") String instanceId) {
        InstanceInfo instanceInfo = null;
        if (StringUtils.isNotBlank(instanceId)){
            List<Application> sortedApplications = getRegistry().getSortedApplications();
            boolean isBreak = false;
            for (Application sortedApplication : sortedApplications) {
                List<InstanceInfo> instances = sortedApplication.getInstances();
                for (InstanceInfo instance : instances) {
                    String hostName = instance.getHostName();
                    if (hostName.startsWith(instanceId)){
                        instanceInfo = instance;
                        isBreak = true;
                        break;
                    }
                }
                if (isBreak){
                    break;
                }
            }
        }else {
            return ServerResponse.errorMsg("instanceId不能为空");
        }

        return ServerResponse.successMsgData("根据主机名获取实例信息成功", instanceInfo);
    }
    @GetMapping("/apps/{appId}")
    @ApiOperation("根据appId查询实例，可能有多个")
    public ServerResponse getInstancesByAppId(@PathVariable("appId") String appId) {
        Set<InstanceInfo> instanceInfos = new HashSet<>(10);
        if (StringUtils.isNotBlank(appId)){
            List<Application> sortedApplications = getRegistry().getSortedApplications();
            for (Application sortedApplication : sortedApplications) {
                List<InstanceInfo> instances = sortedApplication.getInstances();
                for (InstanceInfo instance : instances) {
                    if (appId.equals(instance.getAppName())){
                        instanceInfos.add(instance);
                    }
                }

            }
        }else {
            return ServerResponse.errorMsg("instanceId不能为空");
        }

        return ServerResponse.successMsgData("根据主机名获取实例信息成功", instanceInfos);
    }

    @GetMapping
    @ResponseBody
    public Map<String, Object> status(HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<>();
        populateBase(request, map);
        populateApps(map);
        StatusInfo statusInfo;
        try {
            //获取当前注册中心的本身的相信信息
            statusInfo = new StatusResource().getStatusInfo();
        } catch (Exception e) {
            //获取当前注册中心的本身的相信信息
            statusInfo = StatusInfo.Builder.newBuilder().isHealthy(false).build();
        }
        map.put("statusInfo", statusInfo);
        populateInstanceInfo(map, statusInfo);
        filterReplicas(map, statusInfo);
        return map;
    }

    /**
     * 获取最后注册的实例
     *
     * @param request
     * @return
     */
    @GetMapping("/lastn")
    public Map<String, Object> lastn(HttpServletRequest request) {
        Map<String, Object> map = new HashMap<>();
        populateBase(request, map);
        PeerAwareInstanceRegistryImpl registry = (PeerAwareInstanceRegistryImpl) getRegistry();
        ArrayList<Map<String, Object>> lastNCanceled = new ArrayList<>();
        List<Pair<Long, String>> list = registry.getLastNCanceledInstances();
        for (Pair<Long, String> entry : list) {
            lastNCanceled.add(registeredInstance(entry.second(), entry.first()));
        }
        map.put("lastNCanceled", lastNCanceled);
        list = registry.getLastNRegisteredInstances();
        ArrayList<Map<String, Object>> lastNRegistered = new ArrayList<>();
        for (Pair<Long, String> entry : list) {
            lastNRegistered.add(registeredInstance(entry.second(), entry.first()));
        }
        map.put("lastNRegistered", lastNRegistered);
        return map;
    }

    private Map<String, Object> registeredInstance(String id, long date) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("id", id);
        map.put("date", new Date(date));
        return map;
    }

    protected void populateBase(HttpServletRequest request, Map<String, Object> model) {
        populateHeader(model);
        populateNavbar(request, model);
    }

    /**
     * 添加注册中心基础信息
     *
     * @param model
     */
    private void populateHeader(Map<String, Object> model) {
        model.put("currentTime", StatusResource.getCurrentTimeAsString());
        model.put("upTime", StatusInfo.getUpTime());
        model.put("environment", ConfigurationManager.getDeploymentContext()
                .getDeploymentEnvironment());
        model.put("datacenter", ConfigurationManager.getDeploymentContext()
                .getDeploymentDatacenter());
        PeerAwareInstanceRegistry registry = getRegistry();
        model.put("registry", registry);
        model.put("isBelowRenewThresold", registry.isBelowRenewThresold() == 1);
        DataCenterInfo info = applicationInfoManager.getInfo().getDataCenterInfo();
        if (info.getName() == DataCenterInfo.Name.Amazon) {
            AmazonInfo amazonInfo = (AmazonInfo) info;
            model.put("amazonInfo", amazonInfo);
            model.put("amiId", amazonInfo.get(AmazonInfo.MetaDataKey.amiId));
            model.put("availabilityZone",
                    amazonInfo.get(AmazonInfo.MetaDataKey.availabilityZone));
            model.put("instanceId", amazonInfo.get(AmazonInfo.MetaDataKey.instanceId));
        }
    }

    private PeerAwareInstanceRegistry getRegistry() {
        return getServerContext().getRegistry();
    }

    private EurekaServerContext getServerContext() {
        return EurekaServerContextHolder.getInstance().getServerContext();
    }

    private void populateNavbar(HttpServletRequest request, Map<String, Object> model) {
        Map<String, String> replicas = new LinkedHashMap<>();
        List<PeerEurekaNode> list = getServerContext().getPeerEurekaNodes().getPeerNodesView();
        for (PeerEurekaNode node : list) {
            try {
                URI uri = new URI(node.getServiceUrl());
                String href = scrubBasicAuth(node.getServiceUrl());
                replicas.put(uri.getHost(), href);
            } catch (Exception ex) {
                // ignore?
            }
        }
        model.put("replicas", replicas.entrySet());
    }

    /**
     * 填所有实例的简单信息
     *
     * @param model
     */
    private void populateApps(Map<String, Object> model) {
        //获取实例的详细信息
        List<Application> sortedApplications = getRegistry().getSortedApplications();
        //封装成简单的信息
        ArrayList<Map<String, Object>> apps = new ArrayList<>();
        for (Application app : sortedApplications) {
            LinkedHashMap<String, Object> appData = new LinkedHashMap<>();
            apps.add(appData);
            appData.put("name", app.getName());
            Map<String, Integer> amiCounts = new HashMap<>();
            Map<InstanceInfo.InstanceStatus, List<Pair<String, String>>> instancesByStatus = new HashMap<>();
            Map<String, Integer> zoneCounts = new HashMap<>();
            for (InstanceInfo info : app.getInstances()) {
                String id = info.getId();
                String url = info.getStatusPageUrl();
                InstanceInfo.InstanceStatus status = info.getStatus();
                String ami = "n/a";
                String zone = "";
                if (info.getDataCenterInfo().getName() == DataCenterInfo.Name.Amazon) {
                    AmazonInfo dcInfo = (AmazonInfo) info.getDataCenterInfo();
                    ami = dcInfo.get(AmazonInfo.MetaDataKey.amiId);
                    zone = dcInfo.get(AmazonInfo.MetaDataKey.availabilityZone);
                }
                Integer count = amiCounts.get(ami);
                if (count != null) {
                    amiCounts.put(ami, count + 1);
                } else {
                    amiCounts.put(ami, 1);
                }
                count = zoneCounts.get(zone);
                if (count != null) {
                    zoneCounts.put(zone, count + 1);
                } else {
                    zoneCounts.put(zone, 1);
                }
                List<Pair<String, String>> list = instancesByStatus.get(status);
                if (list == null) {
                    list = new ArrayList<>();
                    instancesByStatus.put(status, list);
                }
                list.add(new Pair<>(id, url));
            }
            appData.put("amiCounts", amiCounts.entrySet());
            appData.put("zoneCounts", zoneCounts.entrySet());
            ArrayList<Map<String, Object>> instanceInfos = new ArrayList<>();
            appData.put("instanceInfos", instanceInfos);
            for (Iterator<Map.Entry<InstanceInfo.InstanceStatus, List<Pair<String, String>>>> iter = instancesByStatus
                    .entrySet().iterator(); iter.hasNext(); ) {
                Map.Entry<InstanceInfo.InstanceStatus, List<Pair<String, String>>> entry = iter
                        .next();
                List<Pair<String, String>> value = entry.getValue();
                InstanceInfo.InstanceStatus status = entry.getKey();
                LinkedHashMap<String, Object> instanceData = new LinkedHashMap<>();
                instanceInfos.add(instanceData);
                instanceData.put("status", entry.getKey());
                ArrayList<Map<String, Object>> instances = new ArrayList<>();
                instanceData.put("instances", instances);
                instanceData.put("isNotUp", status != InstanceInfo.InstanceStatus.UP);
                for (Pair<String, String> p : value) {
                    LinkedHashMap<String, Object> instance = new LinkedHashMap<>();
                    instances.add(instance);
                    instance.put("id", p.first());
                    String url = p.second();
                    instance.put("url", url);
                    boolean isHref = url != null && url.startsWith("http");
                    instance.put("isHref", isHref);

                }
            }
        }
        model.put("apps", apps);
    }

    /**
     * 填充当前注册中心的信息
     *
     * @param model
     * @param statusInfo
     */
    private void populateInstanceInfo(Map<String, Object> model, StatusInfo statusInfo) {
        InstanceInfo instanceInfo = statusInfo.getInstanceInfo();
        Map<String, String> instanceMap = new HashMap<>();
        instanceMap.put("ipAddr", instanceInfo.getIPAddr());
        instanceMap.put("status", instanceInfo.getStatus().toString());
        if (instanceInfo.getDataCenterInfo().getName() == DataCenterInfo.Name.Amazon) {
            AmazonInfo info = (AmazonInfo) instanceInfo.getDataCenterInfo();
            instanceMap.put("availability-zone",
                    info.get(AmazonInfo.MetaDataKey.availabilityZone));
            instanceMap.put("public-ipv4", info.get(AmazonInfo.MetaDataKey.publicIpv4));
            instanceMap.put("instance-id", info.get(AmazonInfo.MetaDataKey.instanceId));
            instanceMap.put("public-hostname",
                    info.get(AmazonInfo.MetaDataKey.publicHostname));
            instanceMap.put("ami-id", info.get(AmazonInfo.MetaDataKey.amiId));
            instanceMap.put("instance-type",
                    info.get(AmazonInfo.MetaDataKey.instanceType));
        }
        model.put("instanceInfo", instanceMap);
    }

    protected void filterReplicas(Map<String, Object> model, StatusInfo statusInfo) {
        Map<String, String> applicationStats = statusInfo.getApplicationStats();
        if (applicationStats.get("registered-replicas").contains("@")) {
            applicationStats.put("registered-replicas", scrubBasicAuth(applicationStats.get("registered-replicas")));
        }
        if (applicationStats.get("unavailable-replicas").contains("@")) {
            applicationStats.put("unavailable-replicas", scrubBasicAuth(applicationStats.get("unavailable-replicas")));
        }
        if (applicationStats.get("available-replicas").contains("@")) {
            applicationStats.put("available-replicas", scrubBasicAuth(applicationStats.get("available-replicas")));
        }
        model.put("applicationStats", applicationStats);
    }

    private String scrubBasicAuth(String urlList) {
        String[] urls = urlList.split(",");
        String filteredUrls = "";
        for (String u : urls) {
            if (u.contains("@")) {
                filteredUrls += u.substring(0, u.indexOf("//") + 2) + u.substring(u.indexOf("@") + 1, u.length()) + ",";
            } else {
                filteredUrls += u + ",";
            }
        }
        return filteredUrls.substring(0, filteredUrls.length() - 1);
    }
}

